package cn.initcap.concurrency.concurrency.lock;

import java.util.concurrent.locks.StampedLock;

/**
 * @author initcap
 * @date Created in 2018/6/18 AM10:48.
 */
public class StampedLockLock {

    class Point {
        private final StampedLock sl = new StampedLock();
        private double x;
        private double y;

        /**
         * an exclusively locked method
         */
        void move(double deltaX, double deltaY) {
            long stamp = sl.writeLock();
            try {
                x += deltaX;
                y += deltaY;
            } finally {
                sl.unlock(stamp);
            }
        }

        /**
         * 下面是乐观锁案例
         *
         * @return
         */
        double distanceFormOrigin() {
            //获得一个乐观锁
            long stamp = sl.tryOptimisticRead();
            //将两个字段读入本地局部变量
            double currentX = x;
            double currentY = y;
            //检查发出乐观读锁后同时是否有其他写锁发生？
            if (!sl.validate(stamp)) {
                //如果没有，我们再次获得一个读悲观锁
                stamp = sl.readLock();
                try {
                    //将两个字段读入本地局部变量
                    currentX = x;
                    currentY = y;
                } finally {
                    sl.unlockRead(stamp);
                }
            }
            return Math.sqrt(currentX * currentX + currentY * currentY);
        }

        /**
         * 下面是悲观锁的案例
         *
         * @param newX 新值
         * @param newY 旧值
         */
        void moveidAtOrigin(double newX, double newY) {
            long stamp = sl.readLock();
            try {
                //循环，检查当前状态是否符合
                while (x == 0.0 && y == 0.0) {
                    //将读锁转为写锁
                    long ws = sl.tryConvertToWriteLock(stamp);
                    //这是确认转为写锁是否成功
                    if (ws != 0L) {
                        //如果成功，替换票据
                        stamp = ws;
                        //进行运行状态改变
                        x = newX;
                        y = newY;
                        break;
                        //如果不能转换为写锁
                    } else {
                        //我们显示释放读锁
                        sl.unlockRead(stamp);
                        //显示直接进行写锁，然后在通过循环再试
                        stamp = sl.writeLock();
                    }
                }
            } finally {
                //释放读锁活写锁
                sl.unlock(stamp);
            }
        }
    }

}
